# -*- coding: utf-8 -*-
"""
一键把包含一个大Mesh的模型按照指定的切分次数来切分成指定数量的小块（只切分X和Y，不切分Z），并导出至FBX中。
split_times-切分次数，表示在X及Y方向上切割的次数，则总数量是（split_times+1)^2。
用法：
    blender your_file.blend  --background --python split_model_to_parts.py  -- -output_dir /tmp/baked/ -input_model xxx.obj -split_times 4
注意：
    如果导入的模型是fbx，请注意输出目录不能和输入的目录是同一个目录，避免导出的fbx覆盖原始的fbx。
    对于模型比较大（主要针对顶点和面数较多的情况），split_times 可以适当增大
"""
import os, sys
import bpy, bmesh
from mathutils import Vector 

# 获取当前文件的绝对路径，并返回上级目录 
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),  '..'))
sys.path.insert(0,  parent_dir)  # 将上级目录添加到搜索路径首部 
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))

from common_func import export_fbx, parse_cli_args, delete_default_objs


def split_model(obj, split_times):
    print(obj)
    me    = obj.data  
    
    # -----------准备 BMesh-----------
    bm = bmesh.new() 
    bm.from_mesh(me) 

    # -----------参数---------------
    cuts_xyz = (split_times,split_times,0)          # X,Y,Z各切几刀？
    bounds   = [min(v.co[i]  for v in bm.verts)  for i in range(3)]
    extents  = [max(v.co[i]  for v in bm.verts)  - bounds[i] for i in range(3)]
    
    # -----------三轴真·断面切割-----------
    for axis in range(3):
        n_cuts = cuts_xyz[axis]
        if n_cuts==0: continue 
        step   = extents[axis]/(n_cuts+1)
        for i in range(1,n_cuts+1):
            pos      = bounds[axis] + step*i 
            plane_co = Vector([pos if a==axis else bounds[a] for a in range(3)])
            plane_no = Vector([int(a==axis) for a in range(3)])
            ret = bmesh.ops.bisect_plane( 
                bm,
                geom     = bm.faces[:]  + bm.edges[:]  + bm.verts[:], 
                plane_co = plane_co,
                plane_no = plane_no,    
                clear_inner=False,
                clear_outer=False,
            )
            cut_geom   = ret.get('geom_cut',  [])
            if cut_geom:
                cut_edges   = [e for e in cut_geom if isinstance(e,bmesh.types.BMEdge)] 
        
                # ----------一次性去重----------
                cut_verts   = list({v for e in cut_edges for v in e.verts}) 
                
                bmesh.ops.split_edges(bm,  edges=cut_edges)
                bmesh.ops.remove_doubles(bm,  verts=cut_verts, dist=0)
    
    # -----------写回并拆块-----------
    bm.to_mesh(me) 
    bm.free() 
    
    bpy.context.view_layer.objects.active  = obj     #保险起见重新激活一次 
    bpy.ops.object.mode_set(mode='EDIT') 
    bpy.ops.mesh.separate(type='LOOSE')            #现在一定有松散部分！不再 Canceled 
    bpy.ops.object.mode_set(mode='OBJECT') 
    print("Finished – now multiple loose objects.")

   
if __name__ == "__main__":
    args = parse_cli_args()
    out_dir = args.get("output_dir", os.getcwd())
    input_model = args.get("input_model") 
    split_times =  args.get("split_times", 3) 

    if not input_model:
        print(f"您需要输入一个3D模型文件用于切割，输入参数： {args}")
        sys.exit(0)

    os.makedirs(out_dir, exist_ok=True)

    delete_default_objs()

    with bpy.context.temp_override(filepath=str(input_model)): 
        model_type = os.path.basename(input_model).split('.')[-1]
        if model_type.lower() == 'obj':
            bpy.ops.wm.obj_import(filepath=str(input_model)) 
        elif model_type.lower() == 'fbx':
            bpy.ops.import_scene.fbx(filepath=str(input_model))
        else:
            print(f"您输入的模型暂不支持，目前只支持OBJ或FBX，输入参数： {args}")
            sys.exit(0)

        obj = bpy.context.selected_objects[0]

        split_model(obj, int(split_times))

        out_fbx_path=os.path.join(out_dir, os.path.basename(input_model)[0:-len(model_type)]+'fbx')
        export_fbx(out_fbx_path)
